﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UniUI.ConsoleUI
{
    public class ConsoleContainerCtrl : IConsoleFocusControl
    {
        public ConsoleContainerCtrl(IConsoleControl parent)
        {
            this.ParentControl = parent;
        }

        private List<IConsoleControl> ctrls = new List<IConsoleControl>();
        private List<IConsoleFocusControl> activeControls = new List<IConsoleFocusControl>();
        private int tabStop;
        private IConsoleFocusControl activeControl;

        private int _Left;
        public int Left
        {
            get { return _Left; }
            set
            {
                this.InvalidateParent();
                _Left = value;
            }
        }

        private int _Top;
        public int Top
        {
            get { return _Top; }
            set
            {
                this.InvalidateParent();
                _Top = value;
            }
        }

        private int _Width;
        public int Width
        {
            get { return _Width; }
            set
            {
                if (value < _Width)
                {
                    this.InvalidateParent();
                }
                _Width = value;
            }
        }

        private int _Height;
        public int Height
        {
            get { return _Height; }
            set
            {
                if (value < _Height)
                {
                    this.InvalidateParent();
                }
                _Height = value;
            }
        }

        public ConsoleColor BackgroundColor { get; set; }

        public IConsoleControl ParentControl { get; private set; }

        public void Render(ConsoleScreen screen)
        {
            if (dirty)
            {
                dirty = false;
                screen.BackgroundColor = BackgroundColor;
                int absX = this.GetAbsoluteLeft(), absY = this.GetAbsoluteTop();

                lock (invalidateRect)
                {
                    foreach (var rect in invalidateRect)
                    {
                        screen.DrawRect(absX + rect.Left, absY + rect.Top, rect.Width, rect.Height);
                    }
                    invalidateRect.Clear();
                }
            }
            foreach (var ctrl in ctrls)
            {
                ctrl.Render(screen);
            }
        }

        public void AddControl(IConsoleControl ctrl)
        {
            ctrls.Add(ctrl);
            if (ctrl is IConsoleFocusControl)
            {
                activeControls.Add((IConsoleFocusControl)ctrl);
            }
            UpdateActiveCtrl();
        }

        public void RemoveControl(IConsoleControl ctrl)
        {
            ctrls.Remove(ctrl);
            if (ctrl is IConsoleFocusControl)
            {
                activeControls.Remove((IConsoleFocusControl)ctrl);
            }
            UpdateActiveCtrl();
        }

        private void SetNewTabStop(int? newTabStop)
        {
            if (!newTabStop.HasValue)
            {
                if (activeControl == null)
                {
                    return;
                }

                tabStop = 0;
                if (isFocused && activeControl != null)
                {
                    activeControl.LostFocus();
                }
                activeControl = null;
            }
            else
            {
                if (activeControls[newTabStop.Value] == activeControl)
                {
                    return;
                }

                tabStop = newTabStop.Value;
                if (isFocused && activeControl != null)
                {
                    activeControl.LostFocus();
                }
                activeControl = activeControls[tabStop];
                if (isFocused)
                {
                    activeControl.GetFocus();
                }
            }
        }

        private void UpdateActiveCtrl()
        {
            if (activeControls.Count == 0)
            {
                SetNewTabStop(null);
                return;
            }

            int searchTabStop = tabStop;
            if (searchTabStop > activeControls.Count)
            {
                searchTabStop = 0;
            }
            else if (searchTabStop == activeControls.Count)
            {
                searchTabStop -= 1;
            }
            for (int nextTabStop = searchTabStop; nextTabStop < activeControls.Count; ++nextTabStop)
            {
                if (activeControls[nextTabStop].CanGetFocus)
                {
                    SetNewTabStop(nextTabStop);
                    return;
                }
            }
            for (int nextTabStop = 0; nextTabStop < searchTabStop; ++nextTabStop)
            {
                if (activeControls[nextTabStop].CanGetFocus)
                {
                    SetNewTabStop(nextTabStop);
                    return;
                }
            }
            SetNewTabStop(null);
        }

        private void TabNext()
        {
            int searchTabStop = tabStop + 1;
            if (searchTabStop == activeControls.Count)
            {
                searchTabStop = 0;
            }

            for (int nextTabStop = searchTabStop; nextTabStop < activeControls.Count; ++nextTabStop)
            {
                if (activeControls[nextTabStop].CanGetFocus)
                {
                    SetNewTabStop(nextTabStop);
                    return;
                }
            }
            for (int nextTabStop = 0; nextTabStop < searchTabStop; ++nextTabStop)
            {
                if (activeControls[nextTabStop].CanGetFocus)
                {
                    SetNewTabStop(nextTabStop);
                    return;
                }
            }
            SetNewTabStop(null);
        }

        private void TabLast()
        {
            int searchTabStop = tabStop - 1;
            if (searchTabStop == -1)
            {
                searchTabStop = activeControls.Count - 1;
            }

            for (int nextTabStop = searchTabStop; nextTabStop >= 0; --nextTabStop)
            {
                if (activeControls[nextTabStop].CanGetFocus)
                {
                    SetNewTabStop(nextTabStop);
                    return;
                }
            }
            for (int nextTabStop = activeControls.Count - 1; nextTabStop > searchTabStop; --nextTabStop)
            {
                if (activeControls[nextTabStop].CanGetFocus)
                {
                    SetNewTabStop(nextTabStop);
                    return;
                }
            }
            SetNewTabStop(null);
        }

        public void OnKeyPress(ConsoleKeyInfo key, bool isInsertMode)
        {
            if (key.Key == ConsoleKey.Tab)
            {
                if ((key.Modifiers & ConsoleModifiers.Shift) == ConsoleModifiers.Shift)
                {
                    TabLast();
                    return;
                }
                else if (key.Modifiers == 0)
                {
                    TabNext();
                    return;
                }
            }
            if (activeControls.Count > 0)
            {
                activeControls[tabStop].OnKeyPress(key, isInsertMode);
            }
        }

        public int CursorX
        {
            get { return activeControl == null ? 0 : activeControl.CursorX; }
        }

        public int CursorY
        {
            get { return activeControl == null ? 0 : activeControl.CursorY; }
        }

        public bool ShowCursor
        {
            get { return activeControl == null ? false : activeControl.ShowCursor; }
        }

        private bool isFocused;

        public void GetFocus()
        {
            isFocused = true;
            if (activeControl != null)
            {
                activeControl.GetFocus();
            }
        }

        public void LostFocus()
        {
            isFocused = false;
            if (activeControl != null)
            {
                activeControl.LostFocus();
            }
        }

        public bool CanGetFocus
        {
            get { return true; } //TODO not always true
        }

        private bool dirty = true;
        private List<Rectangle> invalidateRect = new List<Rectangle>();

        public void Invalidate(int left, int top, int width, int height)
        {
            dirty = true;
            lock (invalidateRect)
            {
                invalidateRect.Add(new Rectangle { Left = left, Top = top, Width = width, Height = height });
            }

            int rleft = left - this.GetAbsoluteLeft(), rtop = top - this.GetAbsoluteTop();
            int rright = rleft + width, rbottom = rtop + height;
            foreach (var ctrl in ctrls)
            {
                if (ctrl.Left < rright && ctrl.Left + ctrl.Width > rleft &&
                    ctrl.Top < rbottom && ctrl.Top + ctrl.Height > rtop)
                {
                    ctrl.Invalidate(rleft - ctrl.Left, rtop - ctrl.Top, width, height);
                }
            }
        }
    }
}
